#ifndef _13815b6b_e85b_4f64_bf75_bb71b2040741__FILESTREAM_H__
#define _13815b6b_e85b_4f64_bf75_bb71b2040741__FILESTREAM_H__

#include <assert.h>
class FileStream
{
public:
   enum TextEncode { TE_UNKNOW, TE_ASCII, TE_UTF8, TE_UNICODE, TE_UNICODE_BE};

public:
    FileStream() : m_file(0) {};
    FileStream(FILE* file) : m_file(file) {};
    FileStream(const char* filename, const char* mode) : m_file(0) { open(filename, mode); }
    ~FileStream() { close(); }

    operator FILE*() { return m_file; }

public:
    inline static bool rename(const char* oldfname, const char* newfname) { return rename(oldfname, newfname) == 0; }
    inline static bool remove(const char* fname) { return remove(fname) == 0; }

public:
    inline bool isOpen() const { return m_file != NULL; }
    
    TextEncode getTextEncode(const char* fname = NULL) {
        short   bom = 0;
        long       curPos;
        
        if (!m_file && fname) {
            if (!open(fname, "r")) return TE_UNKNOW;
            if (fread(&bom, sizeof(bom), 1, m_file) != 1) { close(); return TE_UNKNOW; }
            close();
        }
        else if (!m_file) {
            assert(m_file); return TE_UNKNOW;
        }
        else {
            if ((curPos = ftell(m_file)) < 0) { close(); return TE_UNKNOW; }
            if (fseek(m_file, 0, SEEK_SET)) { close(); return TE_UNKNOW; }
            if (fread(&bom, sizeof(bom), 1, m_file) != 1) { close(); return TE_UNKNOW; }
            if (curPos && fseek(m_file, curPos, SEEK_SET)) { close(); }
        }

        if (bom == 0xFEFF) return TE_UNICODE;
        else if (bom == 0xFFFE) return TE_UNICODE_BE;
        //else if (bom == 0xBBEF) return UTF8; //EF BB BF
        return TE_ASCII;
    }

    inline bool open(const char* name, const char* mode) { 
#if _MSC_VER > 1200
		 fopen_s(&m_file,name, mode); 
#else
		m_file = fopen(name, mode); 
#endif	
		return m_file != NULL; 
	};
    
    // will automatically eat the bom at the beginning.
    inline TextEncode OpenText(const char* name, const char* mode);

    inline void close() { if (m_file) { fclose(m_file); m_file = NULL; } }
    
    inline bool eof() { return m_file && !!feof(m_file); }
    inline bool error() { return m_file && !!ferror(m_file); }

    inline int   getChar() { assert(m_file); if(m_file) return fgetc(m_file); return EOF;}
    inline char* getStr(char* str, int num) { assert(m_file); if(m_file) return fgets(str, num, m_file); return NULL; }
    inline int   read(void *buffer, size_t size, size_t num = 1) { 
        assert(m_file); if (m_file) return fread(buffer, size, num, m_file); return EOF;
    }

    inline int write(int ch) { assert(m_file); if (m_file) return fputc(ch, m_file); return EOF; }
    inline int write(const char* str) { assert(m_file); if (m_file) return fputs(str, m_file); return EOF; }
    inline int write(const void* buffer, size_t size, size_t count = 1) { 
        assert(m_file); if (m_file) return fwrite(buffer, size, count, m_file); return EOF; 
    }
    inline void flush() { assert(m_file); fflush(m_file); };
    inline bool seek(long offset, int origin = SEEK_SET) { assert(m_file); return m_file && !fseek(m_file, offset, origin); }
    inline long tell() { assert(m_file); if (m_file) return ftell(m_file); return EOF; }
    inline long length() {
        assert(m_file); if (!m_file) return EOF;
        
        long curPos, length;
        
        curPos = ftell(m_file);
		if (curPos < 0) curPos = 0;

        if (fseek(m_file, 0, SEEK_END)) return EOF;
        length = ftell(m_file);
        if (fseek(m_file, curPos, SEEK_SET)) return EOF;

        return length;
    }

    inline int setVBuf(char *buffer, int mode, size_t size) { 
        assert(m_file); if (m_file) return setvbuf(m_file, buffer, mode, size); return EOF; 
    }

	inline void* buf(size_t& size) {
		assert(m_file);
		if (!m_file) return NULL;
		void* buf;
		size_t len = length();
		buf = new char[len];
		if (!buf) return NULL;

		if (1 == fread(buf, len, 1, m_file)) {
			size = len;
			return buf;
		}
		delete[] buf;
		return NULL;
	}
    
private:
    FILE * m_file;
};


#endif //_13815b6b_e85b_4f64_bf75_bb71b2040741__FILESTREAM_H__